#include<sys/stat.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define DELIM " \t"
#define EXIT_CODE 40


#define NONE -1
#define IN_RDIR 1
#define OUT_RDIR 2
#define APP_RDIR 4




int quit = 0;
int lastcode = 0;
extern char** environ;
char commandLine[LINE_SIZE];
char* argv[ARGC_SIZE];
char pwd[LINE_SIZE];
char myenv[LINE_SIZE];
int rdir = NONE;
char* filename;






const char* getUserName()
{
  return getenv("USER");
}

const char* getHostName()
{
  return getenv("HOSTNAME");
}


void getPWD()
{
  //系统接口，获取当前工作路径
  getcwd(pwd,sizeof(pwd));
}

//检查命令中是否有重定向
void  check_rdir(char*  cline)
{
  int i = 0;
  while(cline[i])
  {
    if(cline[i] == '>')
    {
      cline[i++] = '\0';
      if(cline[i] == '>')
      {
        
        rdir = APP_RDIR;
        cline[i++] = '\0';
        while(cline[i] == ' ') i++;
        filename = cline + i; 
      }
      else
      {
        rdir = OUT_RDIR;
        while(cline[i] == ' ') i++;
        filename = cline + i; 
      }
    }
    else if(cline[i] == '<')
    {
      cline[i++] = '\0';
      rdir = IN_RDIR;
      while(cline[i] == ' ') i++;
      filename = cline + i; 
     

    }
    else
    {
      //不做事
    }
    i++;
  }

}


//2.交互
void interact(char* cline,int size)
{
  getPWD();
  printf(LEFT"%s@%s %s"RIGHT""LABLE" ",getUserName(),getHostName(),pwd);
  char *s = fgets(cline,size,stdin);
  assert(s);
  (void)s;

  //保证字符串最后一位是\0而不是\n\0
  cline[strlen(cline)-1] = '\0';
  check_rdir(cline);
}

//3.分割字符串、解析命令行
int splitString(char* _argv[],char* cline)
{
  int i = 0;
  _argv[i++] = strtok(cline,DELIM);
  while(_argv[i++] = strtok(NULL,DELIM));
  return i-1;
}



//4.内建命令的执行
int buildCommand(char* _argv[],int _argc)
{

    if(_argc == 2 && strcmp(_argv[0],"cd") == 0)
    {
      chdir(_argv[1]);
      getPWD();
      sprintf(getenv("PWD"),"%s",pwd);
      return 1;
    }
    else if(_argc == 2 && (strcmp(_argv[0],"export") == 0))
    {
      strcpy(myenv,_argv[1]);
      putenv(myenv);
      return 1;
    }
    else if(_argc == 2 && (strcmp(_argv[0],"echo") == 0))
    {
      if(strcmp(_argv[1],"$?") == 0)
      {
        printf("%d\n",lastcode);
        lastcode = 0;
      }
      else if(*_argv[1] == '$')
      {
        char* tmp = getenv(_argv[1]+1);
        if(tmp)
        printf("%s\n",tmp);
      }
      else
      {
        printf("%s\n",argv[1]);
      }
        return 1;
    }



    //给我们的ls命令加颜色的选项，使ls下的文件更好分辨
    
    if(strcmp(_argv[0],"ls") == 0)
    {
      _argv[_argc++] = "--color";
      _argv[_argc] = NULL;
    }

    return 0;
}


//5.普通命令执行
void nomalExcute(char* _argv[])
{

    pid_t id = fork();
    if(id < 0)
    {
      perror("fork");
      return;
    }
    else if(id == 0)
    {

      if(rdir & IN_RDIR)
      {
        int fd = open(filename,O_RDONLY); 
        dup2(fd,0);
      }
      else if(rdir & OUT_RDIR)
      {
        int fd = open(filename,O_WRONLY|O_TRUNC|O_CREAT,0666); 
        dup2(fd,1);
      }
      else if(rdir & APP_RDIR)
      { 
        int fd = open(filename,O_WRONLY|O_APPEND|O_CREAT,0666); 
        dup2(fd,1);
      }
      //子进程执行执行命令
      execvpe(_argv[0],_argv,environ);
      exit(EXIT_CODE);
    }

    else
    {
      int status;
      pid_t rid = waitpid(id,&status,0);
      if(rid == id) 
      {
        lastcode = WEXITSTATUS(status);
      }
    }
}



int main()
{

  while(!quit)
  {
    rdir = NONE;
    filename = NULL;
    //2.交互
    interact(commandLine,LINE_SIZE);


    //commandLind->"ls -a -l\0" -> "ls" "-a" "-l"
    //3.截取子串
    int argc = splitString(argv,commandLine);

    //4.指令的判断
    //内建命令，本质就是shell内部的一个函数
    int n = buildCommand(argv,argc);

    //5.普通命令的执行
    if(!n)nomalExcute(argv);


    //for(int i = 0; argv[i];i++) printf("[%d]:%s\n",i,argv[i]);

  }

  return 0;
}
